/*
 *  Project:            Network_Tester_v0.1.
 *  Module name:        Tester_Conf.
 *  Description:        Top Module of Network_Tester_hardware.
 *  Last updated date:  2023.04.19.
 *
 *  Copyright (C) 2021-2022 Junnan Li <lijunnan@nudt.edu.cn>.
 *  Copyright and related rights are licensed under the MIT license.
 *
 *  Noted:
 *    1) 134b pkt data definition: 
 *      [133:132] head tag, 2'b01 is head, 2'b10 is tail;
 *      [131:128] valid tag, 4'b1111 means sixteen 8b data is valid;
 *      [127:0]   pkt data, invalid part is padded with 0;
 *
 */

`timescale 1ns / 1ps

module Tester_Conf(
  input                   i_rst_n,
  input                   i_clk,
  input                   i_data_valid,
  input         [133:0]   i_data,
  output  reg   [133:0]   o_data,
  output  reg             o_data_valid,
  output  reg   [133:0]   o_data_send,
  output  reg             o_data_send_valid,
  input                   i_err_data_valid,
  input         [133:0]   i_err_data
);


//* ram;
reg             wren_conf;
reg   [133:0]   din_conf;
(* mark_debug = "true"*)reg   [8:0]     addr_conf, addr_send;
wire  [133:0]   dout_conf, dout_send;

//* fifo;
reg             rden_pkt;
wire            empty_pkt;
wire  [133:0]   dout_pkt;

(* mark_debug = "true"*)reg   [3:0]   state_conf, state_send;
(* mark_debug = "true"*)reg   [31:0]  r_cnt_clk_internal;
(* mark_debug = "true"*)reg           r_tag_start;
localparam    IDLE_S              = 4'd0,
              SEND_RESPOND_S      = 4'd1,
              SEND_RESPOND_2_S    = 4'd2,
              SEND_RESPOND_3_S    = 4'd3,
              SEND_RESPOND_4_S    = 4'd4,
              WR_PKT_S            = 4'd5,
              WAIT_1_S            = 4'd6,
              WAIT_2_S            = 4'd7,
              RD_PKT_S            = 4'd8,
              SET_CNT_CLK_S       = 4'd9,
              SEND_ERR_PKT_S      = 4'd10,
              WAIT_1_FIFO_S       = 4'd11;


always @(posedge i_clk or negedge i_rst_n) begin
  if(!i_rst_n) begin
    wren_conf                     <= 1'b0;
    addr_conf                     <= 9'b0;
    din_conf                      <= 134'b0;
    r_cnt_clk_internal            <= 32'h10000;
    r_tag_start                   <= 1'b0;
    rden_pkt                      <= 1'b0;
    o_data_valid                  <= 1'b0;
    o_data                        <= 134'b0;
    
    state_conf                    <= IDLE_S;
  end
  else begin
    case(state_conf)
      IDLE_S: begin
        addr_conf                 <= 9'd0;
        wren_conf                 <= 1'b0;
        rden_pkt                  <= 1'b0;
        o_data_valid              <= 1'b0;
        state_conf                <= IDLE_S;
        if(i_data_valid == 1'b1 && i_data[31:16] == 16'h9004 && i_data[133:132] == 2'b01) begin
          case(i_data[2:0])
            3'd0: begin //* start;
                  r_tag_start     <= 1'b1;
                  state_conf      <= SEND_RESPOND_S;
            end
            3'd1: begin //* stop;
                  r_tag_start     <= 1'b0;
                  state_conf      <= SEND_RESPOND_S;
            end
            3'd2: state_conf      <= WR_PKT_S;  //* wr pkt_ram;
            3'd3: state_conf      <= WAIT_1_S;  //* rd pkt_ram;
            3'd4: state_conf      <= SET_CNT_CLK_S;
          endcase
        end
        else if(empty_pkt == 1'b0) begin
          rden_pkt                <= 1'b1;
          state_conf              <= SEND_ERR_PKT_S;
        end
        else begin
          state_conf              <= IDLE_S;
        end
      end
      WR_PKT_S: begin
        addr_conf                 <= addr_conf + 9'd1;
        wren_conf                 <= i_data_valid;
        din_conf                  <= i_data;
        if(i_data[133:132] == 2'b10) begin
          state_conf              <= SEND_RESPOND_S;
        end
      end
      SET_CNT_CLK_S: begin
        `ifdef SIM_ENV
            r_cnt_clk_internal    <= i_data[16+:32];
        `else
          if(i_data[24+:23] == 23'b0) begin
            r_cnt_clk_internal    <= 32'h100;
          end
          else begin
            r_cnt_clk_internal    <= i_data[16+:32];
          end
        `endif
        
        state_conf                <= SEND_RESPOND_S;
      end
      SEND_RESPOND_S: begin
        wren_conf                 <= 1'b0;
        o_data_valid              <= 1'b1;
        o_data                    <= {2'b01,4'hf,48'h0001_0203_0405, 48'h8888_8888_8988,16'h9004,16'h1};
        state_conf                <= SEND_RESPOND_2_S;
      end
      SEND_RESPOND_2_S: begin
        o_data_valid              <= 1'b1;
        o_data                    <= {2'b00,4'hf,96'b0,15'b0,r_tag_start,16'h0};
        state_conf                <= SEND_RESPOND_3_S;
      end
      SEND_RESPOND_3_S: begin
        o_data_valid              <= 1'b1;
        o_data                    <= {2'b00,4'hf,80'b0,r_cnt_clk_internal,16'h0};
        state_conf                <= SEND_RESPOND_4_S;
      end
      SEND_RESPOND_4_S: begin
        o_data_valid              <= 1'b1;
        o_data                    <= {2'b10,4'hf,128'b0};
        state_conf                <= IDLE_S;
      end
      WAIT_1_S: begin
        addr_conf                 <= 9'd1 + addr_conf;
        state_conf                <= WAIT_2_S;
      end
      WAIT_2_S: begin
        addr_conf                 <= 9'd1 + addr_conf;
        state_conf                <= RD_PKT_S;
        o_data_valid              <= 1'b1;
        o_data                    <= {2'b01,4'hf,48'h0001_0203_0405, 48'h8888_8888_8988,16'h9004,16'h3};
      end
      RD_PKT_S: begin
        addr_conf                 <= 9'd1 + addr_conf;
        o_data_valid              <= 1'b1;
        o_data                    <= dout_send;
        if(dout_send[133:132]==2'b10) begin
          state_conf              <= IDLE_S;
        end
      end
      SEND_ERR_PKT_S: begin 
        o_data_valid              <= 1'b1;
        o_data                    <= dout_pkt;
        if(dout_pkt[133:132] == 2'b10) begin
          state_conf              <= WAIT_1_FIFO_S;
          rden_pkt                <= 1'b0;
        end
      end
      WAIT_1_FIFO_S: begin
        o_data_valid              <= 1'b0;
        state_conf                <= IDLE_S;
      end
      default: begin
        state_conf                <= IDLE_S;
      end
    endcase
  end
end


//* r_cur_cnt_clk;
reg   [31:0]    r_cur_cnt_clk;
always @(posedge i_clk or negedge i_rst_n) begin
  if(!i_rst_n) begin
    addr_send                     <= 9'b0;
    r_cur_cnt_clk                 <= 32'h10000;
    o_data_send_valid             <= 1'b0;
    o_data_send                   <= 134'b0;
    
    state_send                    <= IDLE_S;
  end
  else begin
    case(state_send)
      IDLE_S: begin
          o_data_send_valid       <= 1'b0;
          r_cur_cnt_clk           <= (r_tag_start == 1'b0)? 32'b0: (32'd1 + r_cur_cnt_clk);
          
          if(r_tag_start == 1'b1 && r_cur_cnt_clk == r_cnt_clk_internal) begin
            addr_send             <= 9'd1;
            state_send            <= WAIT_1_S;
          end
      end
      WAIT_1_S: begin
        addr_send                 <= 9'd1 + addr_send;
        state_send                <= WAIT_2_S;
      end
      WAIT_2_S: begin
        addr_send                 <= 9'd1 + addr_send;
        state_send                <= RD_PKT_S;
      end
      RD_PKT_S: begin
        addr_send                 <= 9'd1 + addr_send;
        o_data_send_valid         <= 1'b1;
        o_data_send               <= (o_data_send_valid == 1'b0)? {2'b01,dout_send[131:0]}:
                                                              dout_send;
        if(dout_send[133:132]==2'b10) begin
          state_send              <= IDLE_S;
          r_cur_cnt_clk           <= 32'b0;
        end
      end
      default: begin
        state_send                <= IDLE_S;
      end
    endcase
  end
end


ram_134b_512 ram_pkt (
  .clka   (i_clk              ),
  .wea    (wren_conf          ),
  .addra  (addr_conf          ),
  .dina   (din_conf           ),
  .douta  (dout_conf          ),
  .clkb   (i_clk              ),
  .web    (1'b0               ),
  .addrb  (addr_send          ),
  .dinb   (134'b0             ),
  .doutb  (dout_send          )
);

fifo_134b_512 fifo_pkt(
  .clk    (i_clk),            // input wire clk
  .srst   (!i_rst_n),         // input wire srst
  .din    (i_err_data),       // input wire [133 : 0] din
  .wr_en  (i_err_data_valid), // input wire wr_en
  .rd_en  (rden_pkt),         // input wire rd_en
  .dout   (dout_pkt),         // output wire [133 : 0] dout
  .full   (),                 // output wire full
  .empty  (empty_pkt)         // output wire empty
);

endmodule
